package com.jf.anmocker.plugin.mockcore.asm

import com.jf.mocker.anotations.MethodMachWay
import com.jf.mocker.anotations.MethodMockType


class AsmMockManager {

    companion object {
        fun getInstance() = InstanceHolder.instance
    }

    object InstanceHolder {
        val instance = AsmMockManager()
    }

    fun checkForMethodMock(targetMethod: AnMockerMethodNode, classMocker: AnMockerClzNode?) {
        classMocker?.getMethodMockers()?.forEach { mocker ->
            mocker.takeIf { it.isMethodMocker() && it.name == targetMethod.name }?.let {
                val rst = applyMethod(targetMethod, it)
                println("applyMethod[${targetMethod.name}] for class[${classMocker.getTargetClass()}] rst >>> $rst")
            }
        }
    }

    private fun applyMethod(targetMethod: AnMockerMethodNode, methodMockerNode: AnMockerMethodNode): Boolean {
        val methodMockInfo = methodMockerNode.getMethodMockInfo() ?: return false
        if (targetMethod.name != methodMockInfo.name) {
            return false
        } else if (!checkMethodOnlyMach(targetMethod, methodMockInfo)) {
            return false
        }
        return when (methodMockerNode.getMethodMockInfo()?.mockType) {
            MethodMockType.Replace -> replaceMethod(targetMethod, methodMockerNode)
            MethodMockType.Before -> insertMethodBefore(targetMethod, methodMockerNode)
            MethodMockType.After -> insertMethodAfter(targetMethod, methodMockerNode)
            else -> false
        }
    }

    private fun checkMethodOnlyMach(
        targetMethod: AnMockerMethodNode,
        methodMockInfo: MethodAnoEntity?
    ): Boolean {
        if(methodMockInfo == null){
            return false
        }
        if (methodMockInfo.machWay == MethodMachWay.ONLY) {
            //targetMethod.parameters.get(0).name
            val rst = AnMockerFilter.isSameParams(targetMethod.desc, methodMockInfo.params)
            print("checkMethodOnlyMach >>> ${targetMethod.desc} : ${methodMockInfo.params} rst = $rst")
            return rst
        }
        return true
    }

    private fun replaceMethod(
        targetMethod: AnMockerMethodNode,
        methodMockInfo: AnMockerMethodNode
    ): Boolean {
        if(methodMockInfo.getMethodMockInfo()?.name == null){
            return false
        }
        val replaceMethod = AsmMockerCache.getInstance().getClassMocker(methodMockInfo.name!!)
        if(replaceMethod != null){
            //targetMethod.instructions.clear()
            //targetMethod.instructions.add(replaceMethod.getMethodMockers())
        }

        return false
    }

    private fun insertMethodBefore(
        targetMethod: AnMockerMethodNode,
        methodMockInfo: AnMockerMethodNode
    ): Boolean {
        return false
    }

    private fun insertMethodAfter(
        targetMethod: AnMockerMethodNode,
        methodMockInfo: AnMockerMethodNode
    ): Boolean {
        return false
    }
}